// Linux操作系统内核（kernel）利用文件描述符（file descriptor）来访问文件
// 文件描述符是一个非负整数，是一个用于描述被打开文件的索引值，它指向该文件的相关信息记录表
// 当内核打开一个现存文件或创建一个新文件时，就会返回一个文件描述符

// 文件描述符:0（standard input）1（standard output）2（standard error）
// 文件描述符的有效范围是0到OPEN_MAX。一般来说，每个进程最多可以打开64个文件（0～63）​
// 文件描述符是由无符号整数表示的句柄，进程使用它来标识打开的文件
// 文件描述符与包括相关信息（如文件的打开模式、文件的位置类型、文件的初始类型等）的文件对象相关联，这些信息被称做文件的上下文。
// 重定向：标准输入、输出及出错除了可以是默认的键盘或显示器外，还可以是文件或其他命令的输出

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// flags是用于描述文件打开方式的参数,CREAT需要配合mode参数一起使用,mode参数可省略
// mode用于指定所创建文件的权限
#define IMPORTANT_FLAGS O_RDONLY | O_WRONLY | O_RDWR | O_APPEND | O_CREAT
#define IMPORTANT_MODES S_IRWXU | S_IRWXG | S_IRWXO // 分别细分S_IR---/S_IW---/S_IX---

#define FLAGS O_WRONLY | O_CREAT | O_TRUNC                   // 只写，文件不存在则创建，文件长度截短为0
#define MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // 创建文件权限读写和执行，组用户/其它用户读和执行
int main()
{
    // 打开文件 int open(const char* pathname,int flags,mode_t mode);
    // 成功返回文件描述符，失败返回-1
    const char *path_name;
    int fd;
    char pn[30];
    printf("输入路径名(<30个字符)：");
    gets(pn);
    path_name = pn;
    if ((fd = open(path_name, FLAGS, MODE)) == -1)
    {
        printf("错误：文件打开失败!\n");
        exit(1);
    }
    printf("成功：文件打开成功!\n");
    printf("fd=%d\n", fd);
    // 每个已打开的文件都有一个与其相关联的“当前文件位移量”​，它是一个非负整数，用以度量从文件开始处计算的字节数
    // 通常，读、写操作都从当前文件位移量处开始，并使位移量增加所读或写的字节数
    // 按系统默认设置，当打开一个文件时，除非指定O_APPEND选择项，否则该位移量被设置为0，即文件开头
    // off_t lseek(int fd,off_t offset,int whence)
    // whence 移动字节参考的文职 -> SEEK_SET(文件开头)/SEEK_CUR(当前)/SEEK_END(文件末尾)
    // lseek成功执行时返回新的文件位移量，为此可以用下列方式确定一个打开文件的当前位移量
    if (close(fd) == -1)
        printf("文件关闭错误!\n");
    else
        printf("文件已关闭!\n");
    return 0;
}